﻿using ControllerManageServer.PacketParsing;
using Leeder.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ControllerManageServer.Handler
{
    abstract class SocketClientHandler
    {
        #region 声明变量

        /// <summary>
        /// 日志对象
        /// </summary>
        protected LogOperate logObj;
        /// <summary>
        /// ip地址
        /// </summary>
        public string IPAddress { get; set; }
        /// <summary>
        /// 端口号
        /// </summary>
        public int Port { get; set; }
        /// <summary>
        /// socket对象
        /// </summary>
        private Socket socket;
        /// <summary>
        /// 当前连接状态
        /// </summary>
        private bool connectionState;

        #endregion 声明变量

        #region 事件定义

        /// <summary>
		/// 数据接收事件
		/// </summary>
		public event Action<byte[]> OnDataReceiveEvent;
        /// <summary>
        /// 连接状态事件
        /// </summary>
        public event Action<bool> OnConnectionStateChangeEvent;

        #endregion 事件定义

        /// <summary>
        /// 构造
        /// </summary>
        public SocketClientHandler() { }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public SocketClientHandler(string ip, int port)
        {
            this.IPAddress = ip;
            this.Port = port;
        }

        /// <summary>
        /// 启动
        /// </summary>
        public void Start()
        {
            logObj = Log.GetLogger(GetType());
            connectionState = true;
            Connection();
        }

        /// <summary>
        /// 连接
        /// </summary>
        private void Connection()
        {
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(IPAddress, Port);
                OnConnectionStateChange(true);
                logObj.Debug($"{IPAddress}:{Port}连接成功!\r\n");
                DefaultPacketParsing packet = new DefaultPacketParsing();
                packet.SocketObj = socket;
                socket.BeginReceive(packet.ReceiveBuffer, 0, packet.ReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveData), packet);
            }
            catch (Exception ex)
            {
                logObj.Error(ExceptionType.BusinessException, ex);
                OnConnectionStateChange(false);
            }
        }

        /// <summary>
        /// 异步接收
        /// </summary>
        /// <param name="asyncResult"></param>
        private void ReceiveData(IAsyncResult asyncResult)
        {
            DefaultPacketParsing packet = asyncResult.AsyncState as DefaultPacketParsing;
            try
            {
                int receiveLength = packet.SocketObj.EndReceive(asyncResult);
                if (receiveLength == 0) return;
                List<byte> fullData = new List<byte>();
                if (packet.PendingPacket != null && packet.PendingPacket.Length > 0)
                {
                    fullData.AddRange(packet.PendingPacket);
                }
                byte[] tmp = new byte[receiveLength];
                Array.Copy(packet.ReceiveBuffer, 0, tmp, 0, receiveLength);
                fullData.AddRange(tmp);
                packet.PendingPacket = ParsingPacket(fullData.ToArray());
                packet.ReceiveBuffer = new byte[2 * 1024];
                socket.BeginReceive(packet.ReceiveBuffer, 0, packet.ReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveData), packet);
            }
            catch (Exception ex)
            {
                logObj.Error(ExceptionType.BusinessException, ex, "receive data exception");
                Reset();
            }
        }

        /// <summary>
        /// 分包处理
        /// </summary>
        protected abstract byte[] ParsingPacket(byte[] iDatas);

        /// <summary>
        /// 发送数据
        /// </summary>
        public bool SendData(byte[] iDatas)
        {
            try
            {
                int position = 0, length = iDatas.Length;
                while (position < length)
                {
                    position += socket.Send(iDatas, position, length - position, SocketFlags.None);
                }
            }
            catch (Exception ex)
            {
                logObj.Error(ExceptionType.BusinessException, ex, "send data exception\r\n");
                Reset();
                return false;
            }
            return true;
        }

        /// <summary>
        /// 重置
        /// </summary>
        public void Reset()
        {
            // socket为空或者没连上
            while (socket == null || !socket.Connected)
            {
                try
                {
                    Disconnection();
                    Thread.Sleep(1500);
                    Connection();
                }
                catch (Exception ex)
                {
                    logObj.Error(ExceptionType.BusinessException, ex);
                }
            }
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        private void Disconnection()
        {
            try
            {
                if (socket != null)
                {
                    socket.Close();
                    socket = null;
                }
            }
            catch { }
            logObj.Debug($"{IPAddress}:{Port}连接关闭!\r\n");
            OnConnectionStateChange(false);
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            Disconnection();
        }

        /// <summary>
        /// 数据接收
        /// </summary>
        /// <param name="iDatas"></param>
        protected void OnDataReceive(byte[] iDatas)
        {
            OnDataReceiveEvent?.BeginInvoke(iDatas, null, null);
        }

        /// <summary>
        /// 当前连接状态
        /// </summary>
        /// <param name="iState"></param>
        protected void OnConnectionStateChange(bool iState)
        {
            if (iState == connectionState) return;
            connectionState = iState;
            OnConnectionStateChangeEvent?.BeginInvoke(connectionState, null, null);
        }
    }
}
